using System;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq.Expressions;

using E = System.Linq.Expressions.Expression;

namespace ReactiveLang.ExpressionConverter.Util
{
    /// <summary>
    /// A base class for classes implementing visitor pattern with a result over the System.Linq.Expressions.Expression class.
    /// </summary>
    internal class ExpressionVisitor<TResult>
    {
        /// <summary>
        /// Initializes a new instance of the C1.LiveLinq.Expressions.ExpressionVisitor&lt;TResult&gt; class.
        /// </summary>
        protected ExpressionVisitor()
        {
        }

        /// <summary>
        /// This method is called if there is no implementation of Union in the derived class.
        /// </summary>
        /// <param name="exp">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult DefaultVisit(E exp)
        {
            Contract.Requires(exp != null);
            string msg = string.Format("Unsupported expression: '{0}' ({1})", exp, exp.GetType().Name);
            throw new NotSupportedException(msg);
        }

        /// <summary>
        /// Union a binary expression.
        /// </summary>
        /// <param name="exp">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult VisitBinary(BinaryExpression exp)
        {
            return DefaultVisit(exp);
        }

        /// <summary>
        /// Union a conditional expression.
        /// </summary>
        /// <param name="exp">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult VisitConditional(ConditionalExpression exp)
        {
            return DefaultVisit(exp);
        }

        /// <summary>
        /// Union a constant expression.
        /// </summary>
        /// <param name="exp">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult VisitConstant(ConstantExpression exp)
        {
            return DefaultVisit(exp);
        }

        /// <summary>
        /// Union an invocation expression.
        /// </summary>
        /// <param name="exp">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult VisitInvocation(InvocationExpression exp)
        {
            return DefaultVisit(exp);
        }

        /// <summary>
        /// Union a lambda expression.
        /// </summary>
        /// <param name="exp">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult VisitLambda(LambdaExpression exp)
        {
            return DefaultVisit(exp);
        }

        /// <summary>
        /// Union a list initialization expression.
        /// </summary>
        /// <param name="exp">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult VisitListInit(ListInitExpression exp)
        {
            return DefaultVisit(exp);
        }

        /// <summary>
        /// Union a member expression.
        /// </summary>
        /// <param name="exp">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult VisitMember(MemberExpression exp)
        {
            return DefaultVisit(exp);
        }

        /// <summary>
        /// Union a member initialization expression.
        /// </summary>
        /// <param name="exp">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult VisitMemberInit(MemberInitExpression exp)
        {
            return DefaultVisit(exp);
        }

        /// <summary>
        /// Union a method call expression.
        /// </summary>
        /// <param name="exp">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult VisitMethodCall(MethodCallExpression exp)
        {
            return DefaultVisit(exp);
        }

        /// <summary>
        /// Union a new array expression.
        /// </summary>
        /// <param name="exp">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult VisitNewArray(NewArrayExpression exp)
        {
            return DefaultVisit(exp);
        }

        /// <summary>
        /// Union a new object expression.
        /// </summary>
        /// <param name="nex">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult VisitNew(NewExpression nex)
        {
            return DefaultVisit(nex);
        }

        /// <summary>
        /// Union a parameter expression.
        /// </summary>
        /// <param name="exp">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult VisitParameter(ParameterExpression exp)
        {
            return DefaultVisit(exp);
        }

        /// <summary>
        /// Union a type binary expression.
        /// </summary>
        /// <param name="exp">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult VisitTypeBinary(TypeBinaryExpression exp)
        {
            return DefaultVisit(exp);
        }

        /// <summary>
        /// Union a unary expression.
        /// </summary>
        /// <param name="exp">Expression to visit.</param>
        /// <returns>Result of visiting</returns>
        protected virtual TResult VisitUnary(UnaryExpression exp)
        {
            return DefaultVisit(exp);
        }

        protected virtual TResult VisitBlock(BlockExpression exp)
        {
            return DefaultVisit(exp);
        }

        protected virtual TResult VisitDebugInfo(DebugInfoExpression exp)
        {
            return DefaultVisit(exp);
        }

        protected virtual TResult VisitDefault(DefaultExpression exp)
        {
            return DefaultVisit(exp);
        }

        protected virtual TResult VisitDynamic(DynamicExpression exp)
        {
            return DefaultVisit(exp);
        }

        protected virtual TResult VisitGoto(GotoExpression exp)
        {
            return DefaultVisit(exp);
        }

        protected virtual TResult VisitIndex(IndexExpression exp)
        {
            return DefaultVisit(exp);
        }

        protected virtual TResult VisitLabel(LabelExpression exp)
        {
            return DefaultVisit(exp);
        }

        protected virtual TResult VisitLoop(LoopExpression exp)
        {
            return DefaultVisit(exp);
        }

        protected virtual TResult VisitRuntimeVariables(RuntimeVariablesExpression exp)
        {
            return DefaultVisit(exp);
        }

        protected virtual TResult VisitSwitch(SwitchExpression exp)
        {
            return DefaultVisit(exp);
        }

        protected virtual TResult VisitTry(TryExpression exp)
        {
            return DefaultVisit(exp);
        }


        /// <summary>
        /// Union an expression. Calls corresponding Union method.
        /// </summary>
        /// <param name="expr">Expression to visit</param>
        /// <returns>Result of visiting</returns>
        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        protected virtual TResult Visit(E expr)
        {
            Contract.Requires(expr != null);
            switch (expr.NodeType)
            {
                case ExpressionType.Add:
                case ExpressionType.AddAssign:
                case ExpressionType.AddAssignChecked:
                case ExpressionType.AddChecked:
                case ExpressionType.And:
                case ExpressionType.AndAssign:
                case ExpressionType.AndAlso:
                case ExpressionType.Assign:
                case ExpressionType.Coalesce:
                case ExpressionType.Divide:
                case ExpressionType.DivideAssign:
                case ExpressionType.Equal:
                case ExpressionType.ExclusiveOr:
                case ExpressionType.ExclusiveOrAssign:
                case ExpressionType.GreaterThan:
                case ExpressionType.GreaterThanOrEqual:
                case ExpressionType.LeftShift:
                case ExpressionType.LeftShiftAssign:
                case ExpressionType.LessThan:
                case ExpressionType.LessThanOrEqual:
                case ExpressionType.Modulo:
                case ExpressionType.ModuloAssign:
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyAssign:
                case ExpressionType.MultiplyAssignChecked:
                case ExpressionType.MultiplyChecked:
                case ExpressionType.NotEqual:
                case ExpressionType.Or:
                case ExpressionType.OrAssign:
                case ExpressionType.OrElse:
                case ExpressionType.Power:
                case ExpressionType.PowerAssign:
                case ExpressionType.RightShift:
                case ExpressionType.RightShiftAssign:
                case ExpressionType.Subtract:
                case ExpressionType.SubtractAssign:
                case ExpressionType.SubtractAssignChecked:
                case ExpressionType.SubtractChecked:
                    return VisitBinary(expr as BinaryExpression);

                case ExpressionType.ArrayLength:
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked:
                case ExpressionType.Decrement:
                case ExpressionType.Increment:
                case ExpressionType.IsFalse:
                case ExpressionType.IsTrue:
                case ExpressionType.Negate:
                case ExpressionType.NegateChecked:
                case ExpressionType.Not:
                case ExpressionType.OnesComplement:
                case ExpressionType.PostDecrementAssign:
                case ExpressionType.PostIncrementAssign:
                case ExpressionType.PreDecrementAssign:
                case ExpressionType.PreIncrementAssign:
                case ExpressionType.Quote:
                case ExpressionType.Throw:
                case ExpressionType.TypeAs:
                case ExpressionType.Unbox:
                    return VisitUnary(expr as UnaryExpression);

                case ExpressionType.Call:
                    return VisitMethodCall(expr as MethodCallExpression);

                case ExpressionType.Conditional:
                    return VisitConditional(expr as ConditionalExpression);

                case ExpressionType.Invoke:
                    return VisitInvocation(expr as InvocationExpression);

                case ExpressionType.Lambda:
                    return VisitLambda(expr as LambdaExpression);

                case ExpressionType.ListInit:
                    return VisitListInit(expr as ListInitExpression);

                case ExpressionType.MemberAccess:
                    return VisitMember(expr as MemberExpression);

                case ExpressionType.MemberInit:
                    return VisitMemberInit(expr as MemberInitExpression);

                case ExpressionType.New:
                    return VisitNew(expr as NewExpression);

                case ExpressionType.NewArrayBounds:
                case ExpressionType.NewArrayInit:
                    return VisitNewArray(expr as NewArrayExpression);

                case ExpressionType.TypeIs:
                case ExpressionType.TypeEqual:
                    return VisitTypeBinary(expr as TypeBinaryExpression);

                case ExpressionType.Parameter:
                    return VisitParameter(expr as ParameterExpression);

                case ExpressionType.Constant:
                    return VisitConstant(expr as ConstantExpression);

                case ExpressionType.Block:
                    return VisitBlock(expr as BlockExpression);

                case ExpressionType.DebugInfo:
                    return VisitDebugInfo(expr as DebugInfoExpression);

                case ExpressionType.Default:
                    return VisitDefault(expr as DefaultExpression);

                case ExpressionType.Dynamic:
                    return VisitDynamic(expr as DynamicExpression);

                case ExpressionType.Goto:
                    return VisitGoto(expr as GotoExpression);

                case ExpressionType.Index:
                    return VisitIndex(expr as IndexExpression);

                case ExpressionType.Label:
                    return VisitLabel(expr as LabelExpression);

                case ExpressionType.Loop:
                    return VisitLoop(expr as LoopExpression);

                case ExpressionType.RuntimeVariables:
                    return VisitRuntimeVariables(expr as RuntimeVariablesExpression);

                case ExpressionType.Switch:
                    return VisitSwitch(expr as SwitchExpression);
                    
                case ExpressionType.Try:
                    return VisitTry(expr as TryExpression);

                default:
                    return DefaultVisit(expr);
            }
        }
    }
}
